﻿#region Using Statements
using System;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using GameBase.Storage;
#endregion

namespace StarFox
{
    /// <summary>
    /// Basic functionality needed for game settings management.
    /// </summary>
    public sealed class GameSettings
    {
        #region Singleton Pattern

        // the only instance of this class... eager initialization
        static readonly GameSettings instance = new GameSettings();

        // private constructor
        private GameSettings() 
        { 
            // default settings
            vbrEn = defVbrEn;
            brtLvl = defBrtLvl;
            crtLvl = defCrtLvl;
            sfxLvl = defSfxLvl;
            musLvl = defMusLvl;
        }

        /// <summary>
        /// Gets a common instance of this class.
        /// </summary>
        public static GameSettings Instance
        {
            get { return instance; }
        }

        #endregion

        #region Control Options

        // backing fields
        bool vbrEn;

        // default values
        readonly bool defVbrEn = true;

        /// <summary>
        /// Enables or disables gamepad vibration (true -> enabled; false -> disabled)
        /// </summary>
        public bool VibrationEnabled
        {
            get { return vbrEn; }
            set { vbrEn = value; }
        }

        /// <summary>
        /// Sets control settings to their corresponding default values.
        /// </summary>
        public void DefaultControlSettings()
        {
            vbrEn = defVbrEn;
        }

        #endregion

        #region Graphics Options

        // backing fields
        int brtLvl;
        int crtLvl;

        // default values
        readonly int defBrtLvl = 5;
        readonly int defCrtLvl = 5;

        /// <summary>
        /// Gets or sets display brightness level.
        /// </summary>
        public int BrightnessLevel
        {
            get { return brtLvl; }
            set { brtLvl = value; }
        }

        /// <summary>
        /// Gets display default brightness level.
        /// </summary>
        public int DefBrightnessLevel
        {
            get { return defBrtLvl; }
        }

        /// <summary>
        /// Gets or sets display contrast level.
        /// </summary>
        public int ContrastLevel
        {
            get { return crtLvl; }
            set { crtLvl = value; }
        }

        /// <summary>
        /// Gets display default contrast level.
        /// </summary>
        public int DefContrastLevel
        {
            get { return defCrtLvl; }
        }

        /// <summary>
        /// Sets display settings to their corresponding default values.
        /// </summary>
        public void DefaultDisplaySettings()
        {
            brtLvl = defBrtLvl;
            crtLvl = defCrtLvl;
        }

        #endregion

        #region Sound Options

        // backing fields
        int sfxLvl;
        int musLvl;

        // default values
        readonly int defSfxLvl = 10;
        readonly int defMusLvl = 10;

        /// <summary>
        /// Gets or sets sound effects volume level.
        /// </summary>
        public int SfxVolLevel
        {
            get { return sfxLvl; }
            set { sfxLvl = value; }
        }

        /// <summary>
        /// Gets sound effects default volume level.
        /// </summary>
        public int DefSfxVolLevel
        {
            get { return defSfxLvl; }
        }

        /// <summary>
        /// Gets or sets music volume level.
        /// </summary>
        public int MusVolLevel
        {
            get { return musLvl; }
            set { musLvl = value; }
        }

        /// <summary>
        /// Gets music default volume level.
        /// </summary>
        public int DefMusVolLevel
        {
            get { return defMusLvl; }
        }

        /// <summary>
        /// Sets sound settings to their corresponding default values.
        /// </summary>
        public void DefaultSoundSettings()
        {
            sfxLvl = defSfxLvl;
            musLvl = defMusLvl;
        }

        #endregion

        #region Storage Management

        // initialize storage manager
        StorageManager storage = StorageManager.Instance;

        // container and file where to load or save game settings
        string settingsContainer = "AppData";
        string settingsFile = "Settings.xml";

        #region Load Settings

        // game settings loaded correctly
        bool isLoadOk;

        /// <summary>
        /// Loads game settings.
        /// </summary>
        public void LoadSettings()
        {
            // settings loaded correctly
            isLoadOk = false;

            bool auxVbrEn = defVbrEn;
            int auxBrtLvl = defBrtLvl;
            int auxCrtLvl = defCrtLvl;
            int auxSfxLvl = defSfxLvl;
            int auxMusLvl = defMusLvl;

            storage.StorageOperation(settingsContainer,
                (container) =>
                {
                    if (container.FileExists(settingsFile))
                    {
                        using (Stream stream = container.OpenFile(settingsFile, FileMode.Open))
                        {
                            using (XmlReader reader = XmlReader.Create(stream))
                            {
                                #region Settings

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "settings")
                                    {
                                        isLoadOk = true;
                                        break;
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                #endregion

                                #region Controls

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "controls")
                                    {
                                        isLoadOk = true;
                                        break;
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "vibration")
                                    {
                                        reader.Read();
                                        if (reader.NodeType == XmlNodeType.Text)
                                        {
                                            try
                                            {
                                                auxVbrEn = Convert.ToBoolean(reader.Value);
                                            }
                                            catch
                                            {
                                                return;
                                            }

                                            isLoadOk = true;
                                            break;
                                        }
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                #endregion

                                #region Graphics

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "graphics")
                                    {
                                        isLoadOk = true;
                                        break;
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "brightness")
                                    {
                                        reader.Read();
                                        if (reader.NodeType == XmlNodeType.Text)
                                        {
                                            try
                                            {
                                                auxBrtLvl = Convert.ToInt32(reader.Value);
                                            }
                                            catch
                                            {
                                                return;
                                            }

                                            if ((auxBrtLvl >= 0) && (auxBrtLvl <= 10))
                                            {
                                                isLoadOk = true;
                                                break;
                                            }
                                        }
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "contrast")
                                    {
                                        reader.Read();
                                        if (reader.NodeType == XmlNodeType.Text)
                                        {
                                            try
                                            {
                                                auxCrtLvl = Convert.ToInt32(reader.Value);
                                            }
                                            catch
                                            {
                                                return;
                                            }

                                            if ((auxCrtLvl >= 0) && (auxCrtLvl <= 10))
                                            {
                                                isLoadOk = true;
                                                break;
                                            }
                                        }
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                #endregion

                                #region Sounds

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "sounds")
                                    {
                                        isLoadOk = true;
                                        break;
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "sfx")
                                    {
                                        reader.Read();
                                        if (reader.NodeType == XmlNodeType.Text)
                                        {
                                            try
                                            {
                                                auxSfxLvl = Convert.ToInt32(reader.Value);
                                            }
                                            catch
                                            {
                                                return;
                                            }

                                            if ((auxSfxLvl >= 0) && (auxSfxLvl <= 10))
                                            {
                                                isLoadOk = true;
                                                break;
                                            }
                                        }
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "music")
                                    {
                                        reader.Read();
                                        if (reader.NodeType == XmlNodeType.Text)
                                        {
                                            try
                                            {
                                                auxMusLvl = Convert.ToInt32(reader.Value);
                                            }
                                            catch
                                            {
                                                return;
                                            }

                                            if ((auxMusLvl >= 0) && (auxMusLvl <= 10))
                                            {
                                                isLoadOk = true;
                                                break;
                                            }
                                        }
                                    }
                                }

                                #endregion
                            }
                        }
                    }
                });

            if (isLoadOk)
            {
                // settings loaded correctly
                vbrEn = auxVbrEn;
                brtLvl = auxBrtLvl;
                crtLvl = auxCrtLvl;
                sfxLvl = auxSfxLvl;
                musLvl = auxMusLvl;
            }
        }

        #endregion

        #region Save Settings

        /// <summary>
        /// Saves game settings.
        /// </summary>
        public void SaveSettings()
        {
            storage.StorageOperation(settingsContainer,
                (container) =>
                {
                    using (Stream stream = container.OpenFile(settingsFile, FileMode.Create))
                    {
                        XmlWriterSettings settings = new XmlWriterSettings();
                        settings.ConformanceLevel = ConformanceLevel.Document;
                        settings.Indent = true;
                        settings.CloseOutput = true;
                        settings.CheckCharacters = true;

                        using (XmlWriter writer = XmlWriter.Create(stream, settings))
                        {
                            writer.WriteStartDocument();
                            writer.WriteComment(" " + Application.ProductVersion + " ");
                            writer.WriteComment(" GAME SETTINGS ");
                            writer.WriteComment(" do not manually edit this file ");
                            writer.WriteStartElement("settings");
                            writer.WriteStartElement("controls");
                            writer.WriteElementString("vibration", vbrEn.ToString());
                            writer.WriteEndElement();
                            writer.WriteStartElement("graphics");
                            writer.WriteElementString("brightness", brtLvl.ToString());
                            writer.WriteElementString("contrast", crtLvl.ToString());
                            writer.WriteEndElement();
                            writer.WriteStartElement("sounds");
                            writer.WriteElementString("sfx", sfxLvl.ToString());
                            writer.WriteElementString("music", musLvl.ToString());
                            writer.WriteEndElement();
                            writer.WriteEndElement();
                            writer.WriteEndDocument();
                            writer.Flush();
                        }
                    }
                });
        }

        #endregion

        #endregion
    }
}
